-
Notifications
You must be signed in to change notification settings - Fork 0
153. Find Minimum in Rotated Sorted Array #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| right = len(nums) | ||
| while left < right: | ||
| middle = (left + right) // 2 | ||
| if is_index_between_min_val_and_last_val(middle): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
一行なら関数使わなくても良いような気がしました。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if nums[i] <= nums[-1]と書かれた場合に、それがtrueの場合は何を意味するのか、個人的には直感的でないと思ったので関数名に情報を入れました。
| if nums[i] > nums[i + 1]: | ||
| return nums[i + 1] | ||
|
|
||
| raise Exception('unreachable.') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これは何が目的ですか?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seal-azarashi/leetcode#21 (comment)
以前、上記のような議論がありまして、ここに到達しないことが一目で分かるようにこのようにしました。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
勉強になりました、ありがとうございます。
Javaだとコンパイルされるので必須ということだと思うんですが、Pythonでもたしかにそのほうが分かりやすいですね。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unreachable なところに raise を書くことですが、ありかもしれませんが、私はそこまで肯定的ではないです。結構微妙なところだと思います。
まず、一般的に、dead code は避けるものです。
また、Python の場合、返り値があって到達する場合はあってもなくても同じだが return None を書き、到達しない場合は書かないことで、unreachable かの意図は表現されるはずです。
それでは弱く、よほど気になるならば、コメントを一つ書いておくくらいが適切かもしれません。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
コメントありがとうございます。
正直、深い考えがあってこの書き方をしているわけではなかったので、今後気をつけようと思います。
| else: | ||
| right = middle | ||
|
|
||
| return nums[0] if len(nums) == left else nums[left] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
私なら、この除外処理はループの前に
if nums[0]<=nums[-1]:
return nums[0]
とします
求めるべき最小値とそれより右の要素をfalse、それより左の要素をtrueとした時、is_index_between...は最初のリストが真に回転されてる場合はそのtrue/falseと一致して、最初からソートされてる場合には違うものになるからです。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ありがとうございます。
はい、分けて考えるのも一つだと思います。
| return nums[left] | ||
| ``` | ||
| 思考ログ: | ||
| - 35.Search Insert Positionと同じ感じで、半開区間で書いてみた |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
この「半開区間」もなかなか微妙で、何を探しているのかに対して意味が違うし、さらに左右の差もあるんですよ。
右が開いているのだとしても、
[0, 1, 2, 3, 4] に対して、2 を探しているのか、初めて2以上になる境界を探しているのかで、
前者ならば right は 3 で終了っぽいが、後者ならば、境界をその右側で表現して 2 で終了としたりします。
なので、とりあえず、半開と書いて通じた気になっているのよく分からないのですね。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
たとえば、理情の先輩方(同期同士)でも同じ半開区間と呼んでいてこんな感じ。左右のどっちを開いているかも違います。
cafelier (kinaba)
https://topcoder-g-hatena-ne-jp.jag-icpc.org/cafelier/20120703/1341324546.html
int L = 0;
int R = 10_0000_0000;
while( R-L > 1 )
{
int C = (L+R)/2;
(query(C) ? R : L) = C;
}
return R;mametter
https://mametter.hatenablog.com/entry/20161117/p1
l, r = 0, a.size
while l < r
m = l + (r - l) / 2
if a[m] < c
l = m + 1
else
r = m
end
end
lThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
まず何を探しているのでしょうか。
たとえば、
[0, 0, 1, 1, 2, 2] で 1 を探すのだったら何が欲しいのか。
2が欲しい場合、3が欲しい場合、4が欲しい場合、2-3どれかが欲しい場合。
あたりがあります。
それと、[0, 2] で 1 を探すのだったら何が欲しいのか。
-1 やエラーなどが欲しい場合。1 が欲しい場合。
Python の bisect_left は (2, 1)。bisect_right は (4, 1)。
Java の binarySearch は (2-3, ~insertion point)
C++ の upper_bound は (4, 1)。lower_bound は (2, 1)。
その上で、値なり境界なりをどう表現するのか、あることが確定したほうかないことが確定したほうか、境界ならば、多くは境界の右側でしょうが。だいたいここに4通りくらいあります。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left = -1 から始まるパターンは、ここではあんまり見ませんね。ただ、そういう方法もあります。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
この「半開区間」もなかなか微妙で、何を探しているのかに対して意味が違うし、さらに左右の差もあるんですよ。
こちらはすみません、左が閉じていることを前提にした書き方になってました。
正しくは右半開区間ですね。
右が開いているのだとしても、
[0, 1, 2, 3, 4] に対して、2 を探しているのか、初めて2以上になる境界を探しているのかで、
前者ならば right は 3 で終了っぽいが、後者ならば、境界をその右側で表現して 2 で終了としたりします。
確かに思考のプロセスのコメントから、大事な部分が抜けているように思います。
色々な人の過去ログを読んで、以前よりは解像度が高くなった気はしているのですが、まだ自信が持てないので
もう少し詳細に考えていることを書いてみます。
お手数ですが、違和感がある部分をご指摘いただけると幸いです。
(以下は、色々と設定の仕方があると思いますが、今回はこのような方法で探索すると私が決めました)
まず、今回は最小値があるインデックスを探しています。
leftは探索範囲の左側でその場所を含みます。
rightは探索範囲の右側でその場所を含みません。
ピボット(middle)が指す要素が条件を満たすかどうかを判定していきます。
条件は、”最小値があるインデックスか、最小値があるインデックスより右のインデックス”にしました。
探索範囲の補集合について補足すると、leftより左(leftを含まない)は上記の条件を満たさない集合、right以上は上記の条件を満たす集合となっています。
探索範囲を上記条件を満たすように狭めていきます。
ピボットを(left + right) // 2で決めているので、毎回left <= middle < rightとなり探索区間は狭くなります。
left < rightの条件でループしているので、最終的にleft == rightとなってループを抜け、以下のような形で止まります。
(F, F, ..., F, [T), T, T, ..., T)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
設定を変えて、最小値があるインデックスの一つ前の場所を境界として返す場合は、一番右にあるFのインデックスを取ってくるように設計すれば良いと思います。
開区間で書くと以下のような感じでしょうか。
下のケースでは、left=2, right=3で止まり、left以下はF、right以上はTとなっています。
rightを取ってくれば、上で考えた最小値のインデックスを取得する問題に対応します。
nums = [3, 4, 5, 1, 2]
# nums = [1, 2, 3, 4, 5]
left = -1
right = len(nums)
while right - left > 1:
middle = (left + right) // 2
if nums[middle] <= nums[-1]:
right = middle
else:
left = middle
print(left, nums[left]) There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left = -1 から始まるパターンは、ここではあんまり見ませんね。ただ、そういう方法もあります。
実は恥ずかしながら、この取り組みを始める前までは、(discordでもたまに出てくる)めぐる式2分探索という方法を(一応理解したつもりで)テンプレート化して脳死状態で2分探索を書いておりました。。
ただ、-1でインデックスアクセス出来てしまうのが何となく気持ち悪くなり、最近は右半開区間で書くことが多くなりました。
|
|
||
| かかった時間:21min | ||
|
|
||
| 計算量:nums.length=Nとして |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
細かい点ですが、 N = num.length の順番のほうが違和感が少なく感じました。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ありがとうございます。
Nを定義しているわけですから確かに左に来て欲しいですね。
https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/description/